import { defineComponent, createVNode, mergeProps, ref, onMounted, watchEffect, Fragment } from 'vue';
import qrcodegen from './qrcodegen.js';

const QRCodeBaseProps = {
  level: {
    type: String,
    default: undefined
  },
  bgColor: {
    type: String,
    default: undefined
  },
  color: {
    type: String,
    default: undefined
  },
  value: {
    type: String,
    required: true
  },
  style: {
    type: Object
  },
  size: {
    type: Number,
    default: 200
  },
  includeMargin: {
    type: Boolean
  },
  marginSize: {
    type: Number
  },
  icon: {
    type: String
  },
  imageSettings: {
    type: Object
  },
  title: {
    type: String
  }
};
const ERROR_LEVEL_MAP = {
  L: qrcodegen.QrCode.Ecc.LOW,
  M: qrcodegen.QrCode.Ecc.MEDIUM,
  Q: qrcodegen.QrCode.Ecc.QUARTILE,
  H: qrcodegen.QrCode.Ecc.HIGH
};
const DEFAULT_SIZE = 128;
const DEFAULT_LEVEL = 'L';
const DEFAULT_BGCOLOR = '#FFFFFF';
const DEFAULT_FGCOLOR = '#000000';
const DEFAULT_INCLUDEMARGIN = false;
const DEFAULT_IMG_SCALE = 0.25;
const SPEC_MARGIN_SIZE = 4;
const DEFAULT_MARGIN_SIZE = 0;
function generatePath(modules, margin = 0) {
  const ops = [];
  modules.forEach(function (row, y) {
    let start = null;
    row.forEach(function (cell, x) {
      if (!cell && start !== null) {
        // M0 0h7v1H0z injects the space with the move and drops the comma,
        // saving a char per operation
        ops.push(`M${start + margin} ${y + margin}h${x - start}v1H${start + margin}z`);
        start = null;
        return;
      }

      // end of row, clean up or skip
      if (x === row.length - 1) {
        if (!cell) {
          // We would have closed the op above already so this can only mean
          // 2+ light modules in a row.
          return;
        }
        if (start === null) {
          // Just a single dark module.
          ops.push(`M${x + margin},${y + margin} h1v1H${x + margin}z`);
        } else {
          // Otherwise finish the current line.
          ops.push(`M${start + margin},${y + margin} h${x + 1 - start}v1H${start + margin}z`);
        }
        return;
      }
      if (cell && start === null) {
        start = x;
      }
    });
  });
  return ops.join('');
}
function getMarginSize(includeMargin, marginSize) {
  if (marginSize != null) {
    return Math.floor(marginSize);
  }
  return includeMargin ? SPEC_MARGIN_SIZE : DEFAULT_MARGIN_SIZE;
}
function getImageSettings(cells, size, margin, imageSettings) {
  if (imageSettings == null) {
    return null;
  }
  const numCells = cells.length + margin * 2;
  const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE);
  const scale = numCells / size;
  const w = (imageSettings.width || defaultSize) * scale;
  const h = (imageSettings.height || defaultSize) * scale;
  const x = imageSettings.x == null ? cells.length / 2 - w / 2 : imageSettings.x * scale;
  const y = imageSettings.y == null ? cells.length / 2 - h / 2 : imageSettings.y * scale;
  let excavation = null;
  if (imageSettings.excavate) {
    let floorX = Math.floor(x);
    let floorY = Math.floor(y);
    let ceilW = Math.ceil(w + x - floorX);
    let ceilH = Math.ceil(h + y - floorY);
    excavation = {
      x: floorX,
      y: floorY,
      w: ceilW,
      h: ceilH
    };
  }
  return {
    x,
    y,
    h,
    w,
    excavation
  };
}
function excavateModules(modules, excavation) {
  return modules.slice().map((row, y) => {
    if (y < excavation.y || y >= excavation.y + excavation.h) {
      return row;
    }
    return row.map((cell, x) => {
      if (x < excavation.x || x >= excavation.x + excavation.w) {
        return cell;
      }
      return false;
    });
  });
}
const SUPPORTS_PATH2D = function () {
  try {
    new Path2D().addPath(new Path2D());
  } catch (e) {
    return false;
  }
  return true;
}();
const QRCodeCanvas = /* @__PURE__ */ defineComponent({
  name: "QRCodeCanvas",
  props: QRCodeBaseProps,
  setup(props, {
    expose
  }) {
    let {
      value,
      size = DEFAULT_SIZE,
      level = DEFAULT_LEVEL,
      bgColor = DEFAULT_BGCOLOR,
      color = DEFAULT_FGCOLOR,
      includeMargin = DEFAULT_INCLUDEMARGIN,
      marginSize,
      style,
      icon,
      imageSettings,
      ...otherProps
    } = props;
    imageSettings = imageSettings ?? icon ? {
      excavate: true,
      src: undefined
    } : undefined;
    const imgSrc = icon;
    let _canvas;
    let _image;
    expose({
      download: () => {
        const url = _canvas.toDataURL('image/png');
        if ('download' in document.createElement('a')) {
          // 非IE下载
          const elink = document.createElement('a');
          elink.download = '';
          elink.style.display = 'none';
          elink.href = url;
          document.body.appendChild(elink);
          elink.click();
          URL.revokeObjectURL(elink.href); // 释放URL 对象
          document.body.removeChild(elink);
        }
      }
    });

    // We're just using this state to trigger rerenders when images load. We
    // Don't actually read the value anywhere. A smarter use of useEffect would
    // depend on this value.
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const isImgLoaded = ref(false);
    onMounted(() => {
      watchEffect(() => {
        const val = props.value;
        // Always update the canvas. It's cheap enough and we want to be correct
        // with the current state.
        if (_canvas) {
          const ctx = _canvas.getContext('2d');
          if (!ctx) {
            return;
          }
          let cells = qrcodegen.QrCode.encodeText(val, ERROR_LEVEL_MAP[level]).getModules();
          const margin = getMarginSize(includeMargin, marginSize);
          const numCells = cells.length + margin * 2;
          ctx.clearRect(0, 0, numCells, numCells);
          const calculatedImageSettings = getImageSettings(cells, size, margin, imageSettings);
          const image = _image;
          const haveImageToRender = isImgLoaded.value && calculatedImageSettings != null && image !== null && image.complete && image.naturalHeight !== 0 && image.naturalWidth !== 0;
          if (haveImageToRender) {
            if (calculatedImageSettings.excavation != null) {
              cells = excavateModules(cells, calculatedImageSettings.excavation);
            }
          }

          // We're going to scale this so that the number of drawable units
          // matches the number of cells. This avoids rounding issues, but does
          // result in some potentially unwanted single pixel issues between
          // blocks, only in environments that don't support Path2D.
          const pixelRatio = window.devicePixelRatio || 1;
          _canvas.height = _canvas.width = size * pixelRatio;
          const scale = size / numCells * pixelRatio;
          ctx.scale(scale, scale);

          // Draw solid background, only paint dark modules.
          ctx.fillStyle = bgColor;
          ctx.fillRect(0, 0, numCells, numCells);
          ctx.fillStyle = color;
          if (SUPPORTS_PATH2D) {
            // $FlowFixMe: Path2D c'tor doesn't support args yet.
            ctx.fill(new Path2D(generatePath(cells, margin)));
          } else {
            cells.forEach(function (row, rdx) {
              row.forEach(function (cell, cdx) {
                if (cell) {
                  ctx.fillRect(cdx + margin, rdx + margin, 1, 1);
                }
              });
            });
          }
          if (haveImageToRender) {
            ctx.drawImage(image, calculatedImageSettings.x + margin, calculatedImageSettings.y + margin, calculatedImageSettings.w, calculatedImageSettings.h);
          }
        }
      });
    });

    // Ensure we mark image loaded as false here so we trigger updating the
    // canvas in our other effect.
    watchEffect(() => {
      isImgLoaded.value = false;
    });
    const canvasStyle = {
      height: size + 'px',
      width: size + 'px',
      ...style
    };
    return () => createVNode(Fragment, null, [createVNode("canvas", mergeProps({
      "style": canvasStyle,
      "height": size,
      "width": size,
      "ref": el => _canvas = el
    }, otherProps), null), imgSrc != null ? createVNode("img", {
      "src": imgSrc,
      "style": {
        display: 'none'
      },
      "onLoad": () => {
        isImgLoaded.value = true;
      },
      "ref": el => _image = el
    }, null) : null]);
  }
});
const index = /* @__PURE__ */ defineComponent({
  name: 'QRCode',
  props: QRCodeBaseProps,
  setup(props, {
    expose
  }) {
    let canvas;
    const download = () => {
      canvas.download();
    };
    expose({
      download
    });
    return () => createVNode("div", {
      "class": "cm-qrcode",
      "style": {
        "background-color": props.bgColor || DEFAULT_BGCOLOR
      }
    }, [createVNode(QRCodeCanvas, mergeProps(props, {
      "ref": el => canvas = el
    }), null)]);
  }
});

export { QRCodeCanvas, index as default };
